package com.yzs.exam.service.Impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.yzs.exam.common.base.RestResponse;
import com.yzs.exam.common.exception.BusinessException;
import com.yzs.exam.context.WebContext;
import com.yzs.exam.entity.*;
import com.yzs.exam.entity.enums.ExamPaperTypeEnum;
import com.yzs.exam.entity.enums.ExamPaperWeightEnum;
import com.yzs.exam.entity.enums.QuestionTypeEnum;
import com.yzs.exam.entity.exam.*;
import com.yzs.exam.entity.other.KeyValue;
import com.yzs.exam.entity.question.QuestionObject;
import com.yzs.exam.repository.ChapterMapper;
import com.yzs.exam.repository.ExamPaperMapper;
import com.yzs.exam.repository.QuestionMapper;
import com.yzs.exam.service.*;
import com.yzs.exam.service.enums.ActionEnum;
import com.yzs.exam.utils.*;
import com.yzs.exam.viewmodel.admin.exam.ExamPaperEditRequestVM;
import com.yzs.exam.viewmodel.admin.exam.ExamPaperPageRequestVM;
import com.yzs.exam.viewmodel.admin.exam.ExamPaperTitleItemVM;
import com.yzs.exam.viewmodel.admin.question.QuestionEditRequestVM;
import com.yzs.exam.viewmodel.student.dashboard.PaperFilter;
import com.yzs.exam.viewmodel.student.dashboard.PaperInfo;
import com.yzs.exam.viewmodel.student.exam.ExamPaperAttrVM;
import com.yzs.exam.viewmodel.student.exam.ExamPaperPageVM;
import com.yzs.exam.viewmodel.student.exam.ExamPaperQuestionsAttrVM;
import com.yzs.exam.viewmodel.student.exam.ExamPaperRuleVM;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Service
public class ExamPaperServiceImpl extends BaseServiceImpl<ExamPaper> implements ExamPaperService {

    private static AtomicInteger count = new AtomicInteger(0);
    protected final static ModelMapper modelMapper = ModelMapperSingle.Instance();
    private final WebContext webContext;
    private final ChapterMapper chapterMapper;
    private final ExamPaperMapper examPaperMapper;
    private final QuestionMapper questionMapper;
    private final TextContentService textContentService;
    private final QuestionService questionService;
    private final SubjectService subjectService;
    private final ExamPaperQuestionCustomerAnswerService examPaperQuestionCustomerAnswerService;

    @Autowired
    public ExamPaperServiceImpl(ExamPaperMapper examPaperMapper,ChapterMapper chapterMapper,WebContext webContext, QuestionMapper questionMapper, TextContentService textContentService, QuestionService questionService, SubjectService subjectService, ExamPaperQuestionCustomerAnswerService examPaperQuestionCustomerAnswerService) {
        super(examPaperMapper);
        this.webContext = webContext;
        this.chapterMapper = chapterMapper;
        this.examPaperMapper = examPaperMapper;
        this.questionMapper = questionMapper;
        this.textContentService = textContentService;
        this.questionService = questionService;
        this.subjectService = subjectService;
        this.examPaperQuestionCustomerAnswerService=examPaperQuestionCustomerAnswerService;
    }

    //普通试卷分页 -- 试卷请求页vo对象参数：ExamPaperPageRequestVM（start，limit）
    @Override
    public PageInfo<ExamPaper> page(ExamPaperPageRequestVM requestVM) {
        return PageHelper.startPage(requestVM.getPageIndex(), requestVM.getPageSize(), "id desc").doSelectPageInfo(() ->
                examPaperMapper.page(requestVM));
    }

    //任务试卷分页 -- 试卷请求页vo对象参数：ExamPaperPageRequestVM（start，limit）
    @Override
    public PageInfo<ExamPaper> taskExamPage(ExamPaperPageRequestVM requestVM) {
        return PageHelper.startPage(requestVM.getPageIndex(), requestVM.getPageSize(), "id desc").doSelectPageInfo(() ->
                examPaperMapper.taskExamPage(requestVM));
    }

    //学科试卷分类分页 -- 试卷请求页vo对象参数：ExamPaperPageRequestVM（start，limit）
    @Override
    public PageInfo<ExamPaper> studentPage(ExamPaperPageVM requestVM) {
        return PageHelper.startPage(requestVM.getPageIndex(), requestVM.getPageSize(), "id desc").doSelectPageInfo(() ->
                examPaperMapper.studentPage(requestVM));
    }

    /**
     * 编辑试卷 -- 增添、更新
     * @param examPaperEditRequestVM
     * @param user
     * @return
     */
    @Override
    @Transactional
    public ExamPaper savePaperFromVM(ExamPaperEditRequestVM examPaperEditRequestVM, User user) {
        //新增试卷、修改试卷枚举获取
        ActionEnum actionEnum = (examPaperEditRequestVM.getId() == null) ? ActionEnum.ADD : ActionEnum.UPDATE;
        Date now = new Date();
        //获取试卷列表
        List<ExamPaperTitleItemVM> titleItemsVM = examPaperEditRequestVM.getTitleItems();
        List<ExamPaperTitleItemObject> frameTextContentList = frameTextContentFromVM(titleItemsVM);
        String frameTextContentStr = JsonUtil.toJsonStr(frameTextContentList);

        ExamPaper examPaper;
        if (actionEnum == ActionEnum.ADD) {
            examPaper = modelMapper.map(examPaperEditRequestVM, ExamPaper.class);
            TextContent frameTextContent = new TextContent(frameTextContentStr, now);
            textContentService.insertByFilter(frameTextContent);
            examPaper.setFrameTextContentId(frameTextContent.getId());
            examPaper.setCreateTime(now);
            examPaper.setCreateUser(user.getId());
            examPaper.setDeleted(false);
            examPaperFromVM(examPaperEditRequestVM, examPaper, titleItemsVM);
            examPaperMapper.insertSelective(examPaper);
        } else {
            examPaper = examPaperMapper.selectByPrimaryKey(examPaperEditRequestVM.getId());
            TextContent frameTextContent = textContentService.selectById(examPaper.getFrameTextContentId());
            frameTextContent.setContent(frameTextContentStr);
            textContentService.updateByIdFilter(frameTextContent);
            modelMapper.map(examPaperEditRequestVM, examPaper);
            examPaperFromVM(examPaperEditRequestVM, examPaper, titleItemsVM);
            examPaperMapper.updateByPrimaryKeySelective(examPaper);
        }
        return examPaper;
    }

    //试卷对象 -- 通过id查询对应试卷 -- 试卷vo对象
    @Override
    public ExamPaperEditRequestVM examPaperToVM(Integer id) {
        ExamPaper examPaper = examPaperMapper.selectByPrimaryKey(id);
        ExamPaperEditRequestVM vm = modelMapper.map(examPaper, ExamPaperEditRequestVM.class);
        vm.setLevel(examPaper.getGradeLevel());
        //题目
        TextContent frameTextContent = textContentService.selectById(examPaper.getFrameTextContentId());

        List<ExamPaperTitleItemObject> examPaperTitleItemObjects = JsonUtil.toJsonListObject(frameTextContent.getContent(), ExamPaperTitleItemObject.class);
        List<Integer> questionIds = examPaperTitleItemObjects.stream()
                .flatMap(t -> t.getQuestionItems().stream()
                        .map(q -> q.getId()))
                .collect(Collectors.toList());
        List<Question> questions = questionMapper.selectByIds(questionIds);
        List<ExamPaperTitleItemVM> examPaperTitleItemVMS = examPaperTitleItemObjects.stream().map(t -> {
            ExamPaperTitleItemVM tTitleVM = modelMapper.map(t, ExamPaperTitleItemVM.class);
            List<QuestionEditRequestVM> questionItemsVM = t.getQuestionItems().stream().map(i -> {
                Question question = questions.stream().filter(q -> q.getId().equals(i.getId())).findFirst().get();
                QuestionEditRequestVM questionEditRequestVM = questionService.getQuestionEditRequestVM(question);
                questionEditRequestVM.setItemOrder(i.getItemOrder());
                return questionEditRequestVM;
            }).collect(Collectors.toList());
            tTitleVM.setQuestionItems(questionItemsVM);
            return tTitleVM;
        }).collect(Collectors.toList());
        vm.setTitleItems(examPaperTitleItemVMS);
        vm.setScore(ExamUtil.scoreToVM(examPaper.getScore()));
        if (ExamPaperTypeEnum.TimeLimit == ExamPaperTypeEnum.fromCode(examPaper.getPaperType())) {
            List<String> limitDateTime = Arrays.asList(DateTimeUtil.dateFormat(examPaper.getLimitStartTime()), DateTimeUtil.dateFormat(examPaper.getLimitEndTime()));
            vm.setLimitDateTime(limitDateTime);
        }
        return vm;
    }

    //查找成对应时段试卷列表
    @Override
    public List<PaperInfo> indexPaper(PaperFilter paperFilter) {
        return examPaperMapper.indexPaper(paperFilter);
    }


    //查询试卷总数（deleted为有效的）
    @Override
    public Integer selectAllCount() {
        return examPaperMapper.selectAllCount();
    }

    //某月的试卷总数
    @Override
    public List<Integer> selectMothCount() {
        Date startTime = DateTimeUtil.getMonthStartDay();//一个月的第一天
        Date endTime = DateTimeUtil.getMonthEndDay();//一个月的最后一天
        //KeyVaue（String，Integer） -- （第几个月、有几份试卷）
        List<KeyValue> mouthCount = examPaperMapper.selectCountByDate(startTime, endTime);
        List<String> mothStartToNowFormat = DateTimeUtil.MothStartToNowFormat();
        return mothStartToNowFormat.stream().map(md -> {
            KeyValue keyValue = mouthCount.stream().filter(kv -> kv.getName().equals(md)).findAny().orElse(null);
            return null == keyValue ? 0 : keyValue.getValue();
        }).collect(Collectors.toList());
    }

    //试卷vo -- 试卷对象（如果是时段试卷才需要设置限制时间）
    private void examPaperFromVM(ExamPaperEditRequestVM examPaperEditRequestVM, ExamPaper examPaper, List<ExamPaperTitleItemVM> titleItemsVM) {
        Integer gradeLevel = subjectService.levelBySubjectId(examPaperEditRequestVM.getSubjectId());
        Integer questionCount = titleItemsVM.stream()
                .mapToInt(t -> t.getQuestionItems().size()).sum();
        Integer score = titleItemsVM.stream().
                flatMapToInt(t -> t.getQuestionItems().stream()
                        .mapToInt(q -> ExamUtil.scoreFromVM(q.getScore()))
                ).sum();
        examPaper.setQuestionCount(questionCount);
        examPaper.setScore(score);
        examPaper.setGradeLevel(gradeLevel);
        List<String> dateTimes = examPaperEditRequestVM.getLimitDateTime();
        if (ExamPaperTypeEnum.TimeLimit == ExamPaperTypeEnum.fromCode(examPaper.getPaperType())) {
            examPaper.setLimitStartTime(DateTimeUtil.parse(dateTimes.get(0), DateTimeUtil.STANDER_FORMAT));
            examPaper.setLimitEndTime(DateTimeUtil.parse(dateTimes.get(1), DateTimeUtil.STANDER_FORMAT));
        }
    }

    /**
     * 转换成试卷对象列表
     * @param titleItems
     * @return
     */
    private List<ExamPaperTitleItemObject> frameTextContentFromVM(List<ExamPaperTitleItemVM> titleItems) {
        //定义原子操作类 -- 保证线程安全
        AtomicInteger index = new AtomicInteger(1);
        return titleItems.stream().map(t -> {
            ExamPaperTitleItemObject titleItem = modelMapper.map(t, ExamPaperTitleItemObject.class);
            List<ExamPaperQuestionItemObject> questionItems = t.getQuestionItems().stream()
                    .map(q -> {
                        ExamPaperQuestionItemObject examPaperQuestionItemObject = modelMapper.map(q, ExamPaperQuestionItemObject.class);
                        examPaperQuestionItemObject.setItemOrder(index.getAndIncrement());
                        return examPaperQuestionItemObject;
                    })
                    .collect(Collectors.toList());
            titleItem.setQuestionItems(questionItems);
            return titleItem;
        }).collect(Collectors.toList());
    }

    /**
     * 随机生成试卷题目id列表
     * @param model
     * @param user
     * @return
     */
    private List<Integer> getRandomQuetionIds(ExamPaperAttrVM model, User user){ ;
        List<Integer> errorIds = examPaperQuestionCustomerAnswerService.selectErrorIdList(user.getId());
        ExamPaperQuestionsAttrVM questionsAttrVM = new ExamPaperQuestionsAttrVM(model.getSubjectId(), model.getDifficult(), errorIds);
        List<Integer> newIds = questionMapper.selectNotErrorQuestionIds(questionsAttrVM);
        Integer errorNum = model.getErrorQuestionNum();
        Integer newNum = model.getNewQuestionNum();
        List<Integer> ids = ExamUtil.randomNewErrorQuestionIds(errorNum, newNum, errorIds, newIds);
        return ids;
    }

    /**
     * 生成试卷请求编辑vo -- 用于生成试卷对象 -- 智能训练
     * @param model
     * @return
     */
    @Override
    public ExamPaperEditRequestVM getExamPaperEditRequestVM(ExamPaperAttrVM model){
        User user = webContext.getCurrentUser();
        List<Integer> Ids = getRandomQuetionIds(model,user);
        List<Question> questions = questionService.selectQuestionsByIds(Ids);
        String totalScore = ExamUtil.scoreToVM(questions.stream().mapToInt(q -> q.getScore()).sum());//试卷总分
        List<Integer> qTypes = questions.stream().map(q ->
                q.getQuestionType()).distinct().collect(Collectors.toList());//试卷所有类型题名（单选、多选..）
        List<ExamPaperTitleItemVM> titleItems = qTypes.stream().map(qt -> {
            List<Question> titleQuestions =
                    questions.stream().filter(q -> q.getQuestionType() == qt).collect(Collectors.toList());//试卷每个小标题下的题目列表
            List<QuestionEditRequestVM> titleQuestionEditRequestVMs = titleQuestions.stream().map(q -> {
                QuestionEditRequestVM titleQuestionEditRequestVM = questionService.getQuestionEditRequestVM(q);
                return titleQuestionEditRequestVM;
            }).collect(Collectors.toList());//将Quetion转换为vo对象
            ExamPaperTitleItemVM examPaperTitleItemVM = new ExamPaperTitleItemVM();//生产试卷
            examPaperTitleItemVM.setName(QuestionTypeEnum.fromCode(qt).getName());
            examPaperTitleItemVM.setQuestionItems(titleQuestionEditRequestVMs);
            return examPaperTitleItemVM;
        }).collect(Collectors.toList());

        ExamPaperEditRequestVM vm = new ExamPaperEditRequestVM();
        vm.setName(ExamPaperTypeEnum.TelligentTrain.getName() + count.incrementAndGet());
        vm.setLevel(user.getUserLevel());
        vm.setSubjectId(model.getSubjectId());
        vm.setScore(totalScore);
        vm.setPaperType(ExamPaperTypeEnum.TelligentTrain.getCode());
        vm.setSuggestTime(ExamUtil.getExamPaperSuggestTime(questions));
        vm.setTitleItems(titleItems);
        return vm;
    }

    /**
     * 智能组卷
     * @param model
     * @return
     */
    @Override
    public ExamPaperEditRequestVM getExamPaperEditRequestVM(ExamPaperRuleVM model) {
        User user = webContext.getCurrentUser();
        ExamPaperEditRequestVM vm = getIntelligenceExamPaper(model, user, ExamPaperWeightEnum.runCount, ExamPaperWeightEnum.population_size, ExamPaperWeightEnum.expectAdapter);
        return vm;
    }

    /**
     * 智能组卷
     * @param model
     * @param user
     * @param runCount
     * @param populationSize
     * @param expectAdapter
     * @return
     */
    private ExamPaperEditRequestVM getIntelligenceExamPaper(ExamPaperRuleVM model, User user, int runCount, int populationSize,double expectAdapter ){
        Rule rule = getRuleFromVM(model);
        if (rule == null){
            throw new RuntimeException();
        }
        List<List<Question>> lists = getQuestionsByLevelAndChapters(rule.getChapters());
        ExamPaperAlgotithmBean fitness = getFitnessFromPopulation(rule, lists, true, runCount, populationSize, expectAdapter);
        List<Question> questions = fitness.getQuestions();
        String totalScore = ExamUtil.scoreToVM(questions.stream().mapToInt(q -> q.getScore()).sum());
        List<Integer> qTypes = questions.stream().map(q ->
                q.getQuestionType()).distinct().collect(Collectors.toList());
        List<ExamPaperTitleItemVM> titleItems = qTypes.stream().map(qt -> {
            List<Question> titleQuestions =
                    questions.stream().filter(q -> q.getQuestionType() == qt).collect(Collectors.toList());
            List<QuestionEditRequestVM> titleQuestionEditRequestVMs = titleQuestions.stream().map(q -> {
                Chapter chapter = chapterMapper.selectByPrimaryKey(q.getChapterId());
                TextContent textContent = textContentService.selectById(q.getInfoTextContentId());
                QuestionObject questionObject = JsonUtil.toJsonObject(textContent.getContent(), QuestionObject.class);
                QuestionEditRequestVM titleQuestionEditRequestVM = questionService.getQuestionEditRequestVM(q);
                titleQuestionEditRequestVM.setTitle(questionObject.getTitleContent() + "(" + chapter.getName() + ")");
                return titleQuestionEditRequestVM;
            }).collect(Collectors.toList());//将Quetion转换为vo对象
            ExamPaperTitleItemVM examPaperTitleItemVM = new ExamPaperTitleItemVM();//生产试卷
            examPaperTitleItemVM.setName(QuestionTypeEnum.fromCode(qt).getName());
            examPaperTitleItemVM.setQuestionItems(titleQuestionEditRequestVMs);
            return examPaperTitleItemVM;
        }).collect(Collectors.toList());

        ExamPaperEditRequestVM vm = new ExamPaperEditRequestVM();
        vm.setName(ExamPaperTypeEnum.TelligentExam.getName() + count.incrementAndGet());
        vm.setLevel(user.getUserLevel());
        vm.setSubjectId(model.getSubjectId());
        vm.setScore(totalScore);
        vm.setPaperType(ExamPaperTypeEnum.TelligentExam.getCode());
        vm.setSuggestTime(ExamUtil.getExamPaperSuggestTime(questions));
        vm.setTitleItems(titleItems);
        return vm;
    }

    /**
     * 得到不同类型题chapters知识点内的题目，然后添加到列表中
     * @param chapters
     * @return
     */
    private List<List<Question>> getQuestionsByLevelAndChapters(List<Integer> chapters){
        List<List<Question>> questions = new ArrayList<>(5);
        for (int i = QuestionTypeEnum.SingleChoice.getCode();i <= QuestionTypeEnum.ShortAnswer.getCode(); i++){
            questions.add(questionService.selectByLevelTypeChapters(i,chapters));
        }
        return questions;
    }

    private Rule getRuleFromVM(ExamPaperRuleVM model){
        Rule rule = modelMapper.map(model, Rule.class);
        double difficulty = rule.getDifficulty();
        rule.setDifficulty(difficulty * 1.5 / 5);//不限、简单、中等、困难 * 1.5
        Integer total = ExamUtil.getExpectTotalScore(rule);
        rule.setTotalScore(total);
        return  rule;
    }

    /**
     * 种群进化选出最优个体
     * @param rule
     * @param lists
     * @param initFlag
     * @param runCount
     * @param populationSize
     * @param expectAdapter
     * @return
     */
    private ExamPaperAlgotithmBean getFitnessFromPopulation(Rule rule,List<List<Question>> lists,boolean initFlag,int runCount,int populationSize,double expectAdapter){
        int initCount = 0;
        Population population = new Population(populationSize,true,rule,lists);
        System.out.println("---------------------------------------");
        int index = 0;
        for (ExamPaperAlgotithmBean e: population.getPapers()){
            System.out.println("个体：" + ++index + "适应度为：" + e.getAdaptationDegree() + "难度为：" + e.getDifficulty() + "知识点覆盖率为：" + e.getChapterCoverage());
        }
        System.out.println("初始适应度：" + population.getFitness().getAdaptationDegree());
        while (initCount < runCount && population.getFitness().getAdaptationDegree() < expectAdapter){
            initCount++;
            GAUtil.evolvePopulation(population,rule,questionService);
            System.out.println("第 " + initCount + " 次进化，适应度为：" + population.getFitness().getAdaptationDegree());
        }
        System.out.println("进化次数：" + initCount);
        System.out.println(population.getFitness().getAdaptationDegree());
        ExamPaperAlgotithmBean fitness = population.getFitness();
        return fitness;
    }
}
